import re
import traceback
from time import sleep

from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

from decs.relation import relation
from lib.conf import CONFIG
from tools.class_mysql import Conn


class Web:

    def __init__(self, writer):
        self.driver: webdriver.Chrome = None
        # 关联数据字典
        self.relations = {}   # 实例变量
        # 实例化写入excel文件类对象
        self.writer = writer
        # 写入的行号
        self.row = 0

    def open_browser(self, type='gc', time='10'):
        """打开不同类型的浏览器"""
        try:
            if type == 'ff':
                self.driver = webdriver.Firefox()
            elif type == "eg":
                self.driver = webdriver.Edge()
            elif type == 'sa':
                self.driver = webdriver.Safari()
            else:
                self.driver = webdriver.Chrome()
            self.driver.maximize_window()
            self.driver.implicitly_wait(int(time))
            # 写入数据的行号与读取数据的行号是一致的.
            self.__write_excel(True, "打开浏览器成功!")
        except Exception:
            print(traceback.format_exc())
            self.__write_excel(False,  f"打开浏览器失败!{traceback.format_exc()}")

    def open_url(self, url):
        """打开被测网址"""
        if self.driver:
            try:
                self.driver.get(url)
                self.__write_excel(True, f"打开: {url}成功!")
            except Exception:
                print(traceback.format_exc())
                self.__write_excel(False, f"打开: {url}失败!{traceback.format_exc()}")

    def __find_element(self, locator: str):
        """
        元素定位封装
        :param locator: 定位表达式. 要求: locator必须满足  定位方式=值
        e.g.  id=kw  name=kw css=[id="q"]  xpath=//a[text()="hao123"] tag=a  class=s_ipt
        :return: 定位成功返回元素标签, 定位失败返回None
        """
        ele = None
        if locator:
            value = locator[locator.find('=') + 1:]  # 元素定位数据
            if locator.startswith('id'):
                # self.driver.find_element(By.ID, value)
                ele = self.driver.find_element_by_id(value)
            elif locator.startswith('name'):
                ele = self.driver.find_element_by_name(value)
            elif locator.startswith('tag'):
                ele = self.driver.find_element_by_tag_name(value)
            elif locator.startswith('class'):
                ele = self.driver.find_element_by_class_name(value)
            elif locator.startswith('xpath'):
                ele = self.driver.find_element_by_xpath(value)
            elif locator.startswith('css'):
                ele = self.driver.find_element_by_css_selector(value)
            elif locator.startswith('link'):
                ele = self.driver.find_element_by_link_text(value)
            elif locator.startswith('partial'):
                ele = self.driver.find_element_by_partial_link_text(value)

            return ele

    @relation
    def input(self, locator, text: str):  # locator=id=kw   text={order_sn}
        ele = self.__find_element(locator)
        if ele:
            ele.send_keys(text)
            self.__write_excel(True, f"{locator}输入{text}成功!")
            return
        self.__write_excel(False, f"{locator}定位失败, 请确认!")


    def click(self, locator):
        ele = self.__find_element(locator)
        if ele:
            ele.click()
            self.__write_excel(True,  f"{locator}点击成功!")
            return
        self.__write_excel(False, f"{locator}定位失败, 请确认!")


    def quit(self):
        self.driver.quit()

    def refresh(self):
        self.driver.refresh()

    def select(self, locator, visible_text):
        ele = self.__find_element(locator)
        if ele:
            select = Select(ele)
            select.select_by_visible_text(visible_text)
            print(f'标签: {locator}, 选择{visible_text}成功!')
            return True
        print(f'标签: {locator}, 定位失败, 无法进行下拉选择!')

    def into_frame(self, locator):
        ele = self.__find_element(locator)
        if ele:
            self.driver.switch_to.frame(ele)

    def outo_frame(self):
        self.driver.switch_to.default_content()

    def back(self):
        self.driver.back()

    def sleep(self, t='1'):
        sleep(int(t))
        self.__write_excel(True, f"固定等待{t}秒成功!")

    def wait(self, locator, timeout='20', poll='1'):
        try:
            WebDriverWait(self.driver, timeout=int(timeout), poll_frequency=int(poll)).until(
                EC.visibility_of_element_located(locator))
            print(f'元素: {locator}定位成功!')
            return True
        except Exception:
            print(traceback.format_exc())
            print(f'元素: {locator}定位失败,请确认!')

    def get_text(self, locator, key, reg=None):  # id=aaaa, key=order_sn, reg=\d
        ele = self.__find_element(locator)
        if ele:
            text = ele.text
            if reg:
                # 如果存在正则表达式, 则将提取的文本值通过正则表达式进行处理
                text = re.findall(reg, text)[0]
            # 保存关联数据到关联数据字典中
            self.relations[key] = text

            return text

    def switch_window(self, idx='-1'):
        windows = self.driver.window_handles
        self.driver.switch_to.window(windows[int(idx)])

    def assert_sql(self, sql, expect: str):
        """sql断言"""
        self.conn = Conn(**CONFIG)
        sql_result = self.conn.execute_sql(sql, 'one')
        if expect == sql_result:
            print(f'预期: {expect}, 实际: {sql_result}, 一致, 测试通过!')
            return True
        print(f'预期: {expect}, 实际: {sql_result}, 不一致, 测试失败!')

    @relation
    def assert_equal(self, result: str, expect):  # price  1699
        """断言: 预期数据与实际数据一致"""
        if result == expect:
            print(f'预期: {expect}, 实际: {result}, 一致, 测试通过!')
            return True
        print(f'预期: {expect}, 实际: {result}, 不一致, 测试失败!')

    @relation
    def assert_contains(self, result, expect: str):
        """断言: 预期结果是否包含于实际结果中"""
        if result.__contains__(expect):
            print(f'预期: {expect}, 包含在实际: {result}中, 测试通过!')
            return True
        print(f'预期: {expect}, 不包含在实际: {result}中, 测试失败!')

    def assert_element_visible(self, locator):
        """断言: 指定标签是可见的"""
        if self.wait(locator):
            print(f'{locator}存在, 测试通过!')
            return True
        print(f'{locator}不存在, 测试失败!')

    def __write_excel(self, status: bool, text: str):
        """
        将关键字函数执行结果 写入excel工作表中
        :param status: 关键字执行状态:  True=>PASS, False=>FAIL
        :param text: 关键字执行的结果
        :return:
        """
        if status:
            self.writer.write(self.row, 8, 'PASS', '3')
        else:
            self.writer.write(self.row, 8, 'FAIL', '2')
        if len(text) > 512:
            text = text[:512]  # 如果数据过长, 切片截取数据
        self.writer.write(self.row, 9, text)


    # selenium 4.x 中, 不再提供 find_element_by_xxxxx定位方式, 所以可以自己封装该方式
    # def find_element_by_id(self, value):
    #     return self.driver.find_element(By.ID, value)

    # 测试开发:
    # 不在是普通的测试工程师, 通过技术能力, 设计框架或定义了某个工具,
    # 从而方便测试工程师对于测试工作的开展, 提升测试效率, 保障了测试质量

